using System;
using System.Windows.Forms;
using CombatTracker.Creatures;

namespace CombatTracker.Controls
{
  public delegate void CombatantsScreenEvent(CombatantsScreen sender);

  public partial class CombatantsScreen : UserControl
  {
    #region Private Members

    private CreatureList _players;
    private CreatureList _monsters;
    private InitiativeList _initiativeOrder;
    private readonly Random _numberGenerator;
    private InitiativeQuery _initiativeForm;
    private AddPlayerDialog _addPlayerDialog;
    private AddMonsterDialog _addMonsterDialog;

    #endregion

    #region Constructor(s)

    public CombatantsScreen()
    {
      InitializeComponent();

      _initiativeOrder = null;
      _initiativeForm = null;
      _addPlayerDialog = null;
      _addMonsterDialog = null;

      _players = new CreatureList();
      _players.ListChanged += RefreshPlayerList;

      _monsters = new CreatureList();
      _monsters.ListChanged += RefreshMonsterList;

      _numberGenerator = new Random((int) DateTime.Now.Ticks);
    }

    #endregion

    #region Properties

    public InitiativeList InitiativeOrder
    {
      get { return _initiativeOrder; }
    }

    #endregion

    #region Control Events

    private void btnStartCombat_Click(object sender, EventArgs e)
    {
      StartCombat();
    }

    private void btnAddPlayer_Click(object sender, EventArgs e)
    {
      AddPlayer();
    }

    private void btnRemovePlayers_Click(object sender, EventArgs e)
    {
      RemovePlayers();
    }

    private void addPlayerToolStripMenuItem_Click(object sender, EventArgs e)
    {
      AddPlayer();
    }

    private void removeSelectedPlayersToolStripMenuItem_Click(object sender, EventArgs e)
    {
      RemovePlayers();
    }

    private void btnAddMonster_Click(object sender, EventArgs e)
    {
      AddMonsters();
    }

    private void btnRemoveMonster_Click(object sender, EventArgs e)
    {
      RemoveMonsters();
    }

    private void addMonstersNPCsToolStripMenuItem_Click(object sender, EventArgs e)
    {
      AddMonsters();
    }

    private void removeSelectedToolStripMenuItem_Click(object sender, EventArgs e)
    {
      RemoveMonsters();
    }

    #endregion

    #region Custom Events

    public event CombatantsScreenEvent CombatStarted;

    #endregion

    #region Helper Functions

    private void StartCombat()
    {
      CreatureList _combatants = new CreatureList();
      _combatants.AddRange(_players);
      _combatants.AddRange(_monsters);
      _combatants.AddInitiativeRollListener(SetInitiativeScore);

      _initiativeOrder = _combatants.RollInitiative();

      if (CombatStarted != null)
      {
        CombatStarted(this);
      }
    }

    private void SetInitiativeScore(Creature sender)
    {
      switch (sender.Type)
      {
        case CreatureType.Player:
          _initiativeForm = new InitiativeQuery(sender.Name, sender.InitiativeBonus);
          _initiativeForm.ShowDialog();
          while (_initiativeForm.DialogResult != DialogResult.OK)
          {
            _initiativeForm.ShowDialog();
          }

          sender.InitiativeBonus = _initiativeForm.Bonus;
          sender.InitiativeRoll = _initiativeForm.Roll;
          break;
        case CreatureType.Monster:
          sender.InitiativeRoll = _numberGenerator.Next(1, 20) + sender.InitiativeBonus;
          break;
        default:
          throw new ArgumentOutOfRangeException();
      }
    }

    private void AddPlayer()
    {
      if (_addPlayerDialog == null)
      {
        _addPlayerDialog = new AddPlayerDialog();
      }

      if (_addPlayerDialog.ShowDialog() == DialogResult.OK)
      {
        _players.Add(_addPlayerDialog.NewPlayer);
      }
    }

    private void RefreshPlayerList(CreatureList sender)
    {
      lsvPlayers.Items.Clear();

      foreach (Creature creature in sender)
      {
        ListViewItem newItem = new ListViewItem(creature.Name);
        newItem.Tag = creature;
        lsvPlayers.Items.Add(newItem);
      }
    }

    private void RefreshMonsterList(CreatureList sender)
    {
      lsvMonsters.Items.Clear();

      foreach (Creature creature in sender)
      {
        ListViewItem newItem = new ListViewItem(creature.Name);
        newItem.Tag = creature;
        lsvMonsters.Items.Add(newItem);
      }
    }

    private void RemovePlayers()
    {
      if (
        MessageBox.Show("Are you sure you wish to remove the selected players?", "Remove Selected Player(s)",
                        MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK)
      {
        _players.ListChanged -= RefreshPlayerList;

        foreach (ListViewItem lviSelectedItem in lsvPlayers.SelectedItems)
        {
          _players.Remove((Creature) lviSelectedItem.Tag);
        }

        RefreshPlayerList(_players);

        _players.ListChanged += RefreshPlayerList;
      }
    }

    protected override bool ProcessDialogKey(Keys keyData)
    {
      try
      {
        switch (keyData)
        {
          case Keys.Control | Keys.P:
            AddPlayer();
            break;
          case Keys.Control | Keys.M:
            AddMonsters();
            break;
          case Keys.Control | Keys.R:
            RemoveCreatures();
            break;
        }
        return base.ProcessDialogKey(keyData);
      }
      catch (Exception)
      {
        return false;
      }
    }

    private void RemoveCreatures()
    {
      if (lsvPlayers.Focused)
      {
        RemovePlayers();
      }
      else
      {
        RemoveMonsters();
      }
    }

    private void AddMonsters()
    {
      _addMonsterDialog = new AddMonsterDialog();

      if (_addMonsterDialog.ShowDialog() == DialogResult.OK)
      {
        _monsters.AddRange(_addMonsterDialog.Monsters);
      }
    }

    private void RemoveMonsters()
    {
      if (
        MessageBox.Show("Are you sure you wish to remove the selected monster(s)/NPC(s)?",
                        "Remove Selected Monster(s)/NPC(s)", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) ==
        DialogResult.OK)
      {
        _monsters.ListChanged -= RefreshMonsterList;

        foreach (ListViewItem lviSelectedItem in lsvMonsters.SelectedItems)
        {
          _monsters.Remove((Creature) lviSelectedItem.Tag);
        }

        RefreshMonsterList(_monsters);

        _monsters.ListChanged += RefreshMonsterList;
      }
    }

    #endregion
  }
}